/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: fifo buffer
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-06-19     JasonHu           Init
 */

#include <nxos/fifo.h>
#include <nxos/math.h>
#include <nxos/mman.h>
#include <nxos/assert.h>
#include <nxos/utils.h>
#include <nxos/syscall.h>

NX_Error NX_UserFifoInit(NX_UserFifo * fifo, NX_U8 * buffer, NX_Size size)
{
    if (!NX_IsPowerOf2(size))
    {
        return NX_EINVAL;
    }
    
    fifo->buffer = buffer;
    fifo->size = size;
    fifo->in = fifo->out = 0;
    fifo->lock = NX_MutexCreate(0);
    if (fifo->lock == NX_SOLT_INVALID_VALUE)
    {
        fifo->size = 0;
        fifo->buffer = NX_NULL;
        return NX_ErrorGet();
    }
    return NX_EOK;
}

NX_UserFifo * NX_UserFifoCreate(NX_Size size)
{
    NX_U8 * buffer;
    NX_UserFifo * fifo;
    NX_Error err;

    if (!NX_IsPowerOf2(size))
    {
        NX_ASSERT(size < 0x80000000);
        size = NX_RoundupPowOf2(size);
    }

    buffer = NX_MemAlloc(size);
    if (buffer == NX_NULL)
    {
        return NX_NULL;
    }

    fifo = NX_MemAlloc(sizeof(NX_UserFifo));
    if (fifo == NX_NULL)
    {
        NX_MemFree(buffer);
        return NX_NULL;
    }
    
    if ((err = NX_UserFifoInit(fifo, buffer, size)) != NX_EOK)
    {
        NX_MemFree(buffer);
        NX_MemFree(fifo);
        fifo = NX_NULL;
    }
    
    return fifo;
}

void NX_UserFifoDestroy(NX_UserFifo *fifo)
{
    NX_MutexDestroy(fifo->lock);
    NX_MemFree(fifo->buffer);
    NX_MemFree(fifo);
}

NX_Size NX_UserFifoPut(NX_UserFifo * fifo, const NX_U8 * buffer, NX_Size len)
{
    NX_Size minLen;

    if (NX_MutexAcquire(fifo->lock) != NX_EOK)
    {
        return 0;
    }

    len = NX_MIN(len, fifo->size - fifo->in + fifo->out);

    minLen = NX_MIN(len, fifo->size - (fifo->in & (fifo->size - 1)));
    NX_MemCopy((void *)(fifo->buffer + (fifo->in & (fifo->size - 1))), buffer, minLen);
    NX_MemCopy((void *)fifo->buffer, buffer + minLen, len - minLen);

    fifo->in += len;
    
    NX_MutexRelease(fifo->lock);
    return len;
}

NX_Size NX_UserFifoGet(NX_UserFifo * fifo, const NX_U8 * buffer, NX_Size len)
{
    NX_Size minLen;
    
    if (NX_MutexAcquire(fifo->lock) != NX_EOK)
    {
        return 0;
    }

    len = NX_MIN(len, fifo->in - fifo->out);

    minLen = NX_MIN(len, fifo->size - (fifo->out & (fifo->size - 1)));
    NX_MemCopy((void *)buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), minLen);
    NX_MemCopy((void *)(buffer + minLen), fifo->buffer, len - minLen);

    fifo->out += len;
    
    NX_MutexRelease(fifo->lock);
    return len;
}

void NX_UserFifoReset(NX_UserFifo *fifo)
{
    
    if (NX_MutexAcquire(fifo->lock) != NX_EOK)
    {
        return;
    }

    fifo->in = fifo->out = 0;
    NX_MutexRelease(fifo->lock);
}

NX_Size NX_UserFifoLen(NX_UserFifo *fifo)
{
    NX_Size len;
    
    if (NX_MutexAcquire(fifo->lock) != NX_EOK)
    {
        return 0;
    }

    len = fifo->in - fifo->out;
    NX_MutexRelease(fifo->lock);
    return len;
}

NX_Size NX_UserFifoAvaliable(NX_UserFifo *fifo)
{
    NX_Size len;
    
    if (NX_MutexAcquire(fifo->lock) != NX_EOK)
    {
        return 0;
    }

    len = fifo->size - (fifo->in - fifo->out);
    NX_MutexRelease(fifo->lock);
    return len;
}

NX_Solt NX_FifoCreate(NX_Size size)
{
    NX_Solt solt = NX_SOLT_INVALID_VALUE;
    NX_Error err;

    NX_ErrorSet((err = NX_Syscall2(NX_API_FifoCreate, size, &solt)));
    return solt;
}

NX_Size NX_FifoWrite(NX_Solt fifo, const NX_U8 * buffer, NX_Size len)
{
    NX_Error err;
    NX_Size outLen = 0;

    NX_ErrorSet((err = NX_Syscall4(NX_API_FifoWrite, fifo, buffer, len, &outLen)));
    return outLen;
}

NX_Size NX_FifoRead(NX_Solt fifo, const NX_U8 * buffer, NX_Size len)
{
    NX_Error err;
    NX_Size outLen = 0;

    NX_ErrorSet((err = NX_Syscall4(NX_API_FifoRead, fifo, buffer, len, &outLen)));
    return outLen;
}

NX_Size NX_FifoLength(NX_Solt fifo)
{
    NX_Error err;
    NX_Size outLen = 0;

    NX_ErrorSet((err = NX_Syscall2(NX_API_FifoLength, fifo, &outLen)));
    return outLen;
}
